/** @file
  I2C PEI Lib Instance.

  Copyright (c) 1999- 2015, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "I2CDelayPei.h"
#include "I2CIoLibPei.h"
#include "I2CAccess.h"
#include "I2CLibPei.h"
#include <PlatformBaseAddresses.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Library/HobLib.h>
#include <PchRegs/PchRegsPcu.h> 
#include <PchRegs/PchRegsLpss.h> 

#define LPSS_PCI_DEVICE_NUMBER  8

#define R_PCH_LPIO_I2C_MEM_RESETS                 0x804 // Software Reset
#define B_PCH_LPIO_I2C_MEM_RESETS_FUNC            BIT1  // Function Clock Domain Reset
#define B_PCH_LPIO_I2C_MEM_RESETS_APB             BIT0  // APB Domain Reset
#define R_PCH_LPSS_I2C_MEM_PCP                    0x800 // Private Clock Parameters

#define PEI_TEPM_LPSS_DMA_BAR                     0xFE900000
#define PEI_TEPM_LPSS_I2C0_BAR                    0xFE910000
#define PCI_CONFIG_SPACE_SIZE                     0x10000

EFI_GUID  mI2CPeiInitGuid = {
  0x96DED71A, 0xB9E7, 0x4EAD, 0x96, 0x2C, 0x01, 0x69, 0x3C, 0xED, 0x2A, 0x64
};


UINT16 I2CGPIO[]= {
  //
  // 19.1.6  I2C0
  // I2C0_SDA-OD-O -    write 0x2003CC81 to IOBASE + 0x0210
  // I2C0_SCL-OD-O -    write 0x2003CC81 to IOBASE + 0x0200
  //
  0x0210,
  0x0200,

  //
  // 19.1.7  I2C1
  // I2C1_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x01F0
  // I2C1_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x01E0
  //
  0x01F0,
  0x01E0,

  //
  // 19.1.8  I2C2
  // I2C2_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x01D0
  // I2C2_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x01B0
  //
  0x01D0,
  0x01B0,

  //
  // 19.1.9  I2C3
  // I2C3_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x0190
  // I2C3_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x01C0
  // 
  0x0190,
  0x01C0,

  //
  // 19.1.10 I2C4
  // I2C4_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x01A0
  // I2C4_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x0170
  //
  0x01A0,
  0x0170,

  // 
  // 19.1.11 I2C5
  // I2C5_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x0150
  // I2C5_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x0140
  // 
  0x0150,
  0x0140,

  //
  // 19.1.12 I2C6
  // I2C6_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x0180
  // I2C6_SCL-OD-O/I -  write 0x2003CC81 to IOBASE + 0x0160
  // 
  0x0180,
  0x0160
};

/**
  Constructor of this library.

  @param   VOID

  @return  EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
IntelI2CPeiLibConstructor (
  IN EFI_PEI_FILE_HANDLE     FileHandle,
  IN CONST EFI_PEI_SERVICES  **PeiServices
  )
{
  UINTN Index;
  
  for (Index = 0; Index < sizeof(I2CGPIO)/sizeof(UINT16); Index ++) {
    I2CLibPeiMmioWrite32(IO_BASE_ADDRESS+I2CGPIO[Index], 0x2003CC81);
  }

  return EFI_SUCCESS;
}

/**
  Programe all I2C controllers on LPSS. 
  
  I2C0 is function 1 of LPSS. I2C1 is function 2 of LPSS, etc..

  @param   VOID

  @return  EFI_SUCCESS
**/
EFI_STATUS
ProgramPciLpssI2C (
  VOID
  )
{
  UINT32       PmcBase;
  UINT32       DevID;
  UINTN        PciMmBase=0;
  UINTN        Index;
  UINTN        Bar0;
  UINTN        Bar1;
  DEBUG ((EFI_D_INFO, "Pei ProgramPciLpssI2C() Start\n"));
  
  //
  // Set the VLV Function Disable Register to ZERO
  //
  PmcBase         = I2CLibPeiMmioRead32(PciD31F0RegBase + R_PCH_LPC_PMC_BASE) & B_PCH_LPC_PMC_BASE_BAR;
  
  if(I2CLibPeiMmioRead32(PmcBase + R_PCH_PMC_FUNC_DIS)&
      (B_PCH_PMC_FUNC_DIS_LPSS2_FUNC1 | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC2
       | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC3 | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC4 | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC5
       | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC6 | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC7)) {
    I2CLibPeiMmioWrite32(
      PmcBase+R_PCH_PMC_FUNC_DIS,
      I2CLibPeiMmioRead32(PmcBase + R_PCH_PMC_FUNC_DIS)& \
      ~(B_PCH_PMC_FUNC_DIS_LPSS2_FUNC1 | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC2 \
        | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC3 | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC4 \
        | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC5 | B_PCH_PMC_FUNC_DIS_LPSS2_FUNC6|B_PCH_PMC_FUNC_DIS_LPSS2_FUNC7)
      );
    DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C() enable all I2C controllers\n"));
  }

  for(Index = 0; Index < LPSS_PCI_DEVICE_NUMBER; Index ++) {

    PciMmBase = MmPciAddress (
                  0,
                  DEFAULT_PCI_BUS_NUMBER_PCH,
                  PCI_DEVICE_NUMBER_PCH_LPSS_I2C,
                  Index,
                  0
                  );
    DevID =  I2CLibPeiMmioRead32(PciMmBase);

    Bar0 = PEI_TEPM_LPSS_DMA_BAR + (Index * PCI_CONFIG_SPACE_SIZE);
    Bar1 = Bar0 + 0x8000;

    DEBUG((EFI_D_ERROR, "Program Pci Lpss I2C Device  Function=%x DevID=%08x\n", Index, DevID));
    
    //
    // Check if device present
    //
    if (DevID  != 0xFFFFFFFF)  {
      if(!(I2CLibPeiMmioRead32 (PciMmBase + R_PCH_LPSS_I2C_STSCMD) & B_PCH_LPSS_I2C_STSCMD_MSE)) {
        //
        // Program BAR 0
        //
        I2CLibPeiMmioWrite32((UINTN) (PciMmBase + R_PCH_LPSS_I2C_BAR), (UINT32)(Bar0 & B_PCH_LPSS_I2C_BAR_BA));
        
        DEBUG ((EFI_D_ERROR, "I2CBaseAddress1 = 0x%x \n",I2CLibPeiMmioRead32 (PciMmBase+R_PCH_LPSS_I2C_BAR)));
        
        //
        // Program BAR 1
        //
        I2CLibPeiMmioWrite32 ((UINTN)(PciMmBase + R_PCH_LPSS_I2C_BAR1), (UINT32)(Bar1 & B_PCH_LPSS_I2C_BAR1_BA));
        DEBUG ((EFI_D_ERROR, "I2CBaseAddress1 = 0x%x \n",I2CLibPeiMmioRead32(PciMmBase+R_PCH_LPSS_I2C_BAR1)));
        
        //
        // Bus Master Enable & Memory Space Enable
        //
        I2CLibPeiMmioWrite32((UINTN) (PciMmBase + R_PCH_LPSS_I2C_STSCMD), (UINT32)(B_PCH_LPSS_I2C_STSCMD_BME | B_PCH_LPSS_I2C_STSCMD_MSE));
      }
      
      //
      // Release Resets
      //
      I2CLibPeiMmioWrite32 (Bar0 + R_PCH_LPIO_I2C_MEM_RESETS, (B_PCH_LPIO_I2C_MEM_RESETS_FUNC | B_PCH_LPIO_I2C_MEM_RESETS_APB));
      
      //
      // Activate Clocks
      //
      I2CLibPeiMmioWrite32 (Bar0 + R_PCH_LPSS_I2C_MEM_PCP, 0x80020003);//No use for A0

      DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C() Programmed()\n"));
    }

  }
  
  DEBUG ((EFI_D_INFO, "Pei ProgramPciLpssI2C() End\n"));

  return EFI_SUCCESS;
}

/**
  Disable I2C Bus.

  @param I2cControllerIndex   Index of I2C controller.

  @return EFI_SUCCESS
**/
EFI_STATUS
I2cDisable (
  IN UINT8 I2cControllerIndex
  )
{
  UINTN  I2CBaseAddress;
  UINT32 NumTries = 10000;  // 0.1 seconds
  
  I2CBaseAddress = (UINT32) PEI_TEPM_LPSS_I2C0_BAR + I2cControllerIndex * PCI_CONFIG_SPACE_SIZE;
  
  I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_ENABLE, 0);
  while (0 != ( I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_ENABLE_STATUS ) & 1)) {
    MicroSecondDelay (10);
    NumTries --;
    if(0 == NumTries) return EFI_NOT_READY;
  }
  
  return EFI_SUCCESS;
}

/**
  Enable I2C Bus.

  @param I2cControllerIndex   Index of I2C controller.

  @return EFI_SUCCESS
**/
EFI_STATUS
I2cEnable (
  IN UINT8 I2cControllerIndex
  )
{
  UINTN   I2CBaseAddress;
  UINT32 NumTries = 10000;  // 0.1 seconds
  
  I2CBaseAddress = (UINT32) PEI_TEPM_LPSS_I2C0_BAR+ I2cControllerIndex * PCI_CONFIG_SPACE_SIZE;
  I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_ENABLE, 1);
  while (0 == ( I2CLibPeiMmioRead16 ( I2CBaseAddress + R_IC_ENABLE_STATUS ) & 1)) {
    MicroSecondDelay (10);
    NumTries --;
    if(0 == NumTries) return EFI_NOT_READY;
  }
  
  return EFI_SUCCESS;
}


/**
  Set the I2C controller bus clock frequency.

  @param[in] This           Address of the library's I2C context structure
  @param[in] PlatformData   Address of the platform configuration data
  @param[in] BusClockHertz  New I2C bus clock frequency in Hertz

  @retval RETURN_SUCCESS      The bus frequency was set successfully.
  @retval RETURN_UNSUPPORTED  The controller does not support this frequency.

**/
EFI_STATUS
I2cBusFrequencySet (
  IN UINTN   I2CBaseAddress,
  IN UINTN   BusClockHertz,
  IN UINT16  *I2cMode
  )
{
  DEBUG((EFI_D_INFO,"InputFreq BusClockHertz: %d\r\n",BusClockHertz));

  *I2cMode = B_IC_RESTART_EN | B_IC_SLAVE_DISABLE | B_MASTER_MODE;

  //
  //  Set the 100 KHz clock divider
  //
  //  From Table 10 of the I2C specification
  //
  //    High: 4.00 uS
  //    Low:  4.70 uS
  //
  I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_SS_SCL_HCNT, (UINT16)0x214 );
  I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_SS_SCL_LCNT, (UINT16)0x272 );
  
  //
  //    Set the 400 KHz clock divider
  //
  //    From Table 10 of the I2C specification
  //
  //      High: 0.60 uS
  //      Low:  1.30 uS
  //
  I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_FS_SCL_HCNT, (UINT16)0x50 );
  I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_FS_SCL_LCNT, (UINT16)0xAD );

  switch ( BusClockHertz ) {
    case 100 * 1000:
      I2CLibPeiMmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x40);//100K
      *I2cMode |= V_SPEED_STANDARD;
      break;
    case 400 * 1000:
      I2CLibPeiMmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x32);//400K
      *I2cMode |= V_SPEED_FAST;
      break;
    default:
      I2CLibPeiMmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x09);//3.4M
      *I2cMode |= V_SPEED_HIGH;
  }

  return EFI_SUCCESS;
}

/**
  Initializes the host controller to execute I2C commands.

  @param I2cControllerIndex Index of I2C controller in LPSS device. 0 represents I2C0, which is PCI function 1 of LPSS device.   
                                
  @return EFI_SUCCESS       Opcode initialization on the I2C host controller completed.
  @return EFI_DEVICE_ERROR  Device error, operation failed.
**/
EFI_STATUS
I2CInit (
  UINT8 I2cControllerIndex, 
  UINT16 SlaveAddress
  )
{
  EFI_STATUS   Status;
  UINT32       NumTries = 0;
  UINTN        I2CBaseAddress;
  UINT16       I2cMode;
  UINTN        PciMmBase=0;


  PciMmBase = MmPciAddress (
                0,
                DEFAULT_PCI_BUS_NUMBER_PCH,
                PCI_DEVICE_NUMBER_PCH_LPSS_I2C,
                (I2cControllerIndex + 1),
                0
                );

  I2CBaseAddress = I2CLibPeiMmioRead32 (PciMmBase+R_PCH_LPSS_I2C_BAR);

  //
  //  Verify the parameters
  //
  if (1023 < SlaveAddress ) {
    Status =  EFI_INVALID_PARAMETER;
    DEBUG((EFI_D_INFO,"I2cStartRequest Exit with Status %r\r\n", Status));
    return Status;
  }

  if(I2CBaseAddress ==  (PEI_TEPM_LPSS_I2C0_BAR + I2cControllerIndex * PCI_CONFIG_SPACE_SIZE)) {
    return EFI_SUCCESS;
  }
  ProgramPciLpssI2C();

  I2CBaseAddress = (UINT32) (PEI_TEPM_LPSS_I2C0_BAR + I2cControllerIndex * PCI_CONFIG_SPACE_SIZE);
  DEBUG ((EFI_D_ERROR, "I2CBaseAddress = 0x%x \n",I2CBaseAddress));
  NumTries = 10000; // 1 seconds
  while ((1 == ( I2CLibPeiMmioRead32 ( I2CBaseAddress + R_IC_STATUS) & STAT_MST_ACTIVITY ))) {
    MicroSecondDelay(10);
    NumTries --;
    if(0 == NumTries)
      return EFI_DEVICE_ERROR;
  }

  Status = I2cDisable (I2cControllerIndex);
  DEBUG((EFI_D_INFO, "I2cDisable Status = %r\r\n", Status));

  I2cBusFrequencySet(I2CBaseAddress, 400 * 1000, &I2cMode);//Set I2cMode

  I2CLibPeiMmioWrite16(I2CBaseAddress + R_IC_INTR_MASK, 0x0);
  if (0x7F < SlaveAddress) {
    SlaveAddress = (SlaveAddress & 0x3ff ) | IC_TAR_10BITADDR_MASTER;
  }
  I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_TAR, (UINT16) SlaveAddress );
  I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_RX_TL, 0);
  I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_TX_TL, 0 );
  I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_CON, I2cMode);

  Status = I2cEnable(I2cControllerIndex);
  DEBUG((EFI_D_INFO, "I2cEnable Status = %r\r\n", Status));
  I2CLibPeiMmioRead16 ( I2CBaseAddress + R_IC_CLR_TX_ABRT );
  
  return EFI_SUCCESS;
}

/**
  Reads a Byte from I2C Device.
 
  @param  I2cControllerIndex  I2C Bus no to which the I2C device has been connected
  @param  SlaveAddress        Device Address from which the byte value has to be read
  @param  Offset              Offset from which the data has to be read
  @param  *Byte               Address to which the value read has to be stored
                                
  @return  EFI_SUCCESS        If the byte value has been successfully read
  @return  EFI_DEVICE_ERROR   Operation Failed, Device Error
**/
EFI_STATUS ByteReadI2CBasic(
  IN  UINT8 I2cControllerIndex,
  IN  UINT8 SlaveAddress,
  IN  UINTN ReadBytes,
  OUT UINT8 *ReadBuffer,
  IN  UINT8 Start,
  IN  UINT8 End
  )
{

  EFI_STATUS Status;
  UINT32 I2cStatus;
  UINT16 ReceiveData;
  UINT8 *ReceiveDataEnd;
  UINT8 *ReceiveRequest;
  UINT16 RawIntrStat;
  UINTN   I2CBaseAddress;

  I2CBaseAddress = (UINT32)(PEI_TEPM_LPSS_I2C0_BAR + I2cControllerIndex * PCI_CONFIG_SPACE_SIZE);

  Status = EFI_SUCCESS;

  I2CInit(I2cControllerIndex, SlaveAddress);

  ReceiveDataEnd = &ReadBuffer [ReadBytes];
  if(ReadBytes) {
    ReceiveRequest = ReadBuffer;
    DEBUG((EFI_D_INFO,"Read: ---------------%d bytes to RX\r\n",ReceiveDataEnd - ReceiveRequest));

    while ((ReceiveDataEnd > ReceiveRequest) || (ReceiveDataEnd > ReadBuffer)) {
      //
      // Check for NACK
      //
      RawIntrStat = I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_RawIntrStat );
      if ( 0 != (RawIntrStat & I2C_INTR_TX_ABRT )) {
        I2CLibPeiMmioRead16 ( I2CBaseAddress + R_IC_CLR_TX_ABRT );
        Status = RETURN_DEVICE_ERROR;
        DEBUG((EFI_D_INFO,"TX ABRT ,%d bytes hasn't been transferred\r\n",ReceiveDataEnd - ReceiveRequest));
        break;
      }
      
      //
      // Determine if another byte was received
      //
      I2cStatus = I2CLibPeiMmioRead16 ( I2CBaseAddress + R_IC_STATUS );
      if ( 0 != ( I2cStatus & STAT_RFNE )) {
        ReceiveData = I2CLibPeiMmioRead16 ( I2CBaseAddress + R_IC_DATA_CMD );
        *ReadBuffer++ = (UINT8)ReceiveData;
        DEBUG((EFI_D_INFO,"MmioRead32 ,1 byte 0x:%x is received\r\n",ReceiveData));
      }

      if(ReceiveDataEnd==ReceiveRequest) {
        //
        // Waiting the last request to get data and make (ReceiveDataEnd > ReadBuffer) =TRUE.
        //
        continue;
      }
      
      //
      // Wait until a read request will fit
      //
      if ( 0 == ( I2cStatus & STAT_TFNF )) {
        MicroSecondDelay ( 10 );
        continue;
      }
      
      //
      // Issue the next read request
      //
      if(End && Start ) {
        I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_RESTART|B_CMD_STOP);
      } else if (!End && Start ) {
        I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_RESTART);
      } else if (End && !Start ) {
        I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_STOP);
      } else if (!End && !Start ) {
        I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD);
      }
      ReceiveRequest += 1;
    }

  }
  return Status;

}

/**
  Writes a Byte to I2C Device.
 
  @param  I2cControllerIndex   I2C Bus no to which the I2C device has been connected
  @param  SlaveAddress         Device Address from which the byte value has to be written
  @param  Offset               Offset from which the data has to be read
  @param  *Byte                Address to which the value written is stored
                                
  @return  EFI_SUCCESS         IF the byte value has been successfully written
  @return  EFI_DEVICE_ERROR    Operation Failed, Device Error
**/
EFI_STATUS 
ByteWriteI2CBasic(
  IN  UINT8 I2cControllerIndex,
  IN  UINT8 SlaveAddress,
  IN  UINTN WriteBytes,
  IN  UINT8 *WriteBuffer,
  IN  UINT8 Start,
  IN  UINT8 End
  )
{

  EFI_STATUS Status;
  UINT32 I2cStatus;
  UINT8 *TransmitEnd;
  UINT16 RawIntrStat;
  UINTN   I2CBaseAddress;

  I2CBaseAddress = (UINT32)PEI_TEPM_LPSS_I2C0_BAR+ I2cControllerIndex * PCI_CONFIG_SPACE_SIZE;

  Status = EFI_SUCCESS;

  I2CInit(I2cControllerIndex, SlaveAddress);

  TransmitEnd = &WriteBuffer [WriteBytes];
  if( WriteBytes ) {

    DEBUG((EFI_D_INFO,"Write: --------------%d bytes to TX\r\n", TransmitEnd - WriteBuffer));

    while ( TransmitEnd > WriteBuffer) {
      I2cStatus = I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_STATUS);
      RawIntrStat = I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_RawIntrStat);
      if ( 0 != (RawIntrStat & I2C_INTR_TX_ABRT)) {
        I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_CLR_TX_ABRT);
        Status = RETURN_DEVICE_ERROR;
        DEBUG((EFI_D_ERROR,"TX ABRT TransmitEnd:0x%x WriteBuffer:0x%x\r\n", TransmitEnd, WriteBuffer));
        break;
      }
      if (0 == ( I2cStatus & STAT_TFNF)) {
        continue;
      }
      if(End && Start) {
        I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++) | B_CMD_RESTART | B_CMD_STOP);
      } else if (!End && Start ) {
        I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++) | B_CMD_RESTART);
      } else if (End && !Start ) {
        I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++) | B_CMD_STOP);
      } else if (!End && !Start ) {
        I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++));
      }
      
      // Add a small delay to work around some odd behavior being seen.  Without this delay bytes get dropped.
      MicroSecondDelay ( FIFO_WRITE_DELAY );
    }

  }
  
  if(EFI_ERROR(Status)) {
    DEBUG((EFI_D_INFO,"I2cStartRequest Exit with Status %r\r\n",Status));
  }
    
  return Status;
}

/**
  Reads a Byte from I2C Device.
 
  @param  I2cControllerIndex   I2C Bus no to which the I2C device has been connected
  @param  SlaveAddress         Device Address from which the byte value has to be read
  @param  Offset               Offset from which the data has to be read
  @param  ReadBytes            Number of bytes to be read
  @param  *ReadBuffer          Address to which the value read has to be stored
                                
  @return  EFI_SUCCESS       IF the byte value has been successfully read
  @return  EFI_DEVICE_ERROR  Operation Failed, Device Error
**/
EFI_STATUS 
ByteReadI2C(
  IN  UINT8 I2cControllerIndex,
  IN  UINT8 SlaveAddress,
  IN  UINT8 Offset,
  IN  UINTN ReadBytes,
  OUT UINT8 *ReadBuffer
  )
{
  EFI_STATUS        Status;

  DEBUG ((EFI_D_ERROR, "ByteReadI2C:---offset:0x%x\n",Offset));
  Status = ByteWriteI2CBasic(I2cControllerIndex, SlaveAddress, 1, &Offset,TRUE,FALSE);
  Status = ByteReadI2CBasic(I2cControllerIndex, SlaveAddress, ReadBytes, ReadBuffer, TRUE, TRUE);

  return Status;
}

/**
  Writes a Byte to I2C Device.
 
  @param  I2cControllerIndex  I2C Bus no to which the I2C device has been connected
  @param  SlaveAddress        Device Address from which the byte value has to be written
  @param  Offset              Offset from which the data has to be written
  @param  WriteBytes          Number of bytes to be written
  @param  *Byte               Address to which the value written is stored
                                
  @return  EFI_SUCCESS       IF the byte value has been successfully read
  @return  EFI_DEVICE_ERROR  Operation Failed, Device Error
**/
EFI_STATUS ByteWriteI2C(
  IN  UINT8 I2cControllerIndex,
  IN  UINT8 SlaveAddress,
  IN  UINT8 Offset,
  IN  UINTN WriteBytes,
  IN  UINT8 *WriteBuffer
  )
{
  EFI_STATUS        Status;

  DEBUG ((EFI_D_ERROR, "ByteWriteI2C:---offset/bytes/buf:0x%x,0x%x,0x%x,0x%x\n",Offset,WriteBytes,WriteBuffer,*WriteBuffer));
  Status = ByteWriteI2CBasic(I2cControllerIndex, SlaveAddress, 1, &Offset, TRUE, FALSE);
  Status = ByteWriteI2CBasic(I2cControllerIndex, SlaveAddress, WriteBytes, WriteBuffer, FALSE, TRUE);

  return Status;
}
